package errors

// WithMessageCodeStack 返回一个 withStack 类型的错误，为原始错误先后包装注解、
// 错误码（默认为 0 ）和调用栈，最终返回的错误经过三次包装。
func WithMessageCodeStack(err error, msg string, opts ...WithCodeOption) error {
	if err == nil {
		return nil
	}

	// 包装注解。
	wme := &withMessage{
		cause: err,
		msg:   msg,
	}

	// 包装错误码。
	wce := &withCode{
		cause: wme,
	}

	// 不复用 err 变量避免了类型断言。
	for _, opt := range opts {
		opt.applyWithCode(wce)
	}

	// 包装调用栈。
	wse := &withStack{
		wce,
		callers(),
	}

	return wse
}

// WithMessageStack 返回一个 withStack 类型的错误，为原始错误先后包装注解、
// 和调用栈，最终返回的错误经过两次包装。
func WithMessageStack(err error, msg string) error {
	if err == nil {
		return nil
	}

	err = &withMessage{
		cause: err,
		msg:   msg,
	}

	err = &withStack{
		err,
		callers(),
	}

	return err
}

// WithMessage 返回一个 withMessage 类型的错误，为原始错误包装注解。
func WithMessage(err error, msg string) error {
	if err == nil {
		return nil
	}

	return &withMessage{
		cause: err,
		msg:   msg,
	}
}

// WithCode 返回一个 withCode 类型的错误，为原始错误包装错误码。
func WithCode(err error, code int) error {
	if err == nil {
		return nil
	}

	return &withCode{
		cause: err,
		code:  code,
	}
}

// WithStack 返回一个 withStack 类型的错误，为原始错误包装调用栈。
func WithStack(err error) error {
	if err == nil {
		return nil
	}

	return &withStack{
		err,
		callers(),
	}
}

type causer interface {
	Cause() error
}

// Cause 在错误实现 causer 接口的情况下以递归的方式找到并返回原始错误。原始错误
// 被定义为没有实现 causer 接口的错误。
func Cause(err error) error {
	for err != nil {
		cause, ok := err.(causer)

		if !ok {
			break
		}

		if cause.Cause() == nil {
			break
		}

		err = cause.Cause()
	}

	return err
}
